#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <vector>
#include <stdio.h>
#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "dce.h"
#include "utils/myxmlwrap.h"
#include "Log.h"
#include "dispatch_server.h"


using namespace std;

bool stop = false;
vector<string> get_local_ip()
{
	int s;
	struct ifconf conf;
	struct ifreq *ifr;
	char buff[BUFSIZ];
	int num;
	int i;
	vector<string> ip_vec;

	s = socket(PF_INET, SOCK_DGRAM, 0);
	conf.ifc_len = BUFSIZ;
	conf.ifc_buf = buff;

	ioctl(s, SIOCGIFCONF, &conf);
	num = conf.ifc_len / sizeof(struct ifreq);
	ifr = conf.ifc_req;

	for(i=0;i < num;i++) {
		struct sockaddr_in *sin = (struct sockaddr_in *)(&ifr->ifr_addr);

		ioctl(s, SIOCGIFFLAGS, ifr);
		if(((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP)) {
			ip_vec.push_back(inet_ntoa(sin->sin_addr));
		}
		ifr++;
	}
	close(s);
	return ip_vec;
}

bool ip_in_vec(const string &ip, const vector<string> &vec)
{
	for (vector<string>::const_iterator it = vec.begin(); it != vec.end(); it++) {
		if (*it == ip)
			return true;
	}
	return false;
}

int xml_get_node_address(lightxquery &xqry, const string &xpath, address &addr)
{
	string ip;
	int port;
	if (xqry.get_attr_value(xpath, "ip", ip) != 0 || xqry.get_attr_value(xpath, "port", port) != 0)
		return -1;
	addr = address(ip, port);
	return 0;
}

int xml_get_node_ip(lightxquery &xqry, const string &xpath, address &addr)
{
	string ip;
	int port;
	if (xqry.get_attr_value(xpath, "ip", ip) != 0)
		return -1;
	addr = address(ip, -1);
	return 0;
}

int setup_dispatch_server(lightxquery &xqry, vector<dispatch_server *> &svr_vec)
{
	int num_dispatch_server = xqry.how_many("/dumpcalc/nodes/dispatch");
	int num_front_server = xqry.how_many("/dumpcalc/nodes/front");
	
	if (1 != num_dispatch_server) {
		LOG_ERROR_VA("dispatch num error");
		return -1;
	}

	if (0 >= num_front_server) {
		LOG_ERROR_VA("front node num error: %d", num_front_server);
		return -1;
	}
	
	vector<string> local_ip_vec = get_local_ip();
	int num_ip = local_ip_vec.size();
	string svr_ip;
	char xpathbuf[500];
	address addr;
	string path;
	int num_seted_servers = 0;

	//dispatch_server
	dispatch_server *dis_svr = NULL;
	snprintf(xpathbuf, 499, "/dumpcalc/nodes/dispatch[%d]",1);
	if (xml_get_node_address(xqry, xpathbuf, addr) != 0){
		LOG_ERROR_VA("dispatch addr error");
		return -1;
	}
	if (ip_in_vec(addr.ip(), local_ip_vec)){
		dis_svr = new dispatch_server;
		dis_svr->set_listener_address(addr);

		//add front_server to dispatch_server
		for(int i = 0;i != num_front_server;i++){
			snprintf(xpathbuf, 499, "/dumpcalc/nodes/front[%d]",i + 1);
			if (xml_get_node_address(xqry, xpathbuf, addr) == 0){
				dis_svr->add_front_node(addr,i+1);
				LOG_INFO_VA("add front: <%s:%d> weight: %d", addr.ip().c_str(), addr.port(),i+1);
			}
		}
	
		if (dis_svr->setup() == 0) {
			svr_vec.push_back(dis_svr);
			num_seted_servers++;
		} else {
			delete dis_svr;
			LOG_ERROR_VA("dispatch setup error");
			return -1;
		}
	}

	return num_seted_servers;
}

int start_servers(lightxquery &xqry, vector<dispatch_server *> &svr_vec)
{
	if (setup_dispatch_server(xqry, svr_vec) < 0) {
		LOG_ERROR_VA("server setup failed");
		return -1;
	}
	
	// io timeout
	int io_read_timeout, io_write_timeout;
	if (xqry.get_attr_value("/dumpcalc/public/time", "io_read", io_read_timeout) != 0) {
		LOG_ERROR_VA("io_read timeout configure error");
		return -1;
	}
	if(io_read_timeout <= 0){
		LOG_ERROR_VA("io_read error");
		return -1;
	}
	if (xqry.get_attr_value("/dumpcalc/public/time", "io_write", io_write_timeout) != 0) {
		LOG_ERROR_VA("io_write timeout configure error");
		return -1;
	}
	if(io_write_timeout <= 0){
		LOG_ERROR_VA("io_write error");
		return -1;
	}

	server_proxy::io_read_timeout = io_read_timeout;
	server_proxy::io_write_timeout = io_write_timeout;

	//threads num
	int dispatch_num_threads;
	if (xqry.get_attr_value("/dumpcalc/public/thread", "number_of_dispatch_threads", dispatch_num_threads) != 0) {
		LOG_ERROR_VA("dispatch thread number configure error");
		return -1;
	}
	if(dispatch_num_threads <= 0){
		LOG_ERROR_VA("dispatch threads error");
		return -1;
	}

	for (vector<dispatch_server *>::iterator it = svr_vec.begin(); it != svr_vec.end(); it++){
		(*it)->get_thread_pool()->set_max_threads(dispatch_num_threads);
	}

	for (vector<dispatch_server *>::iterator it = svr_vec.begin(); it != svr_vec.end(); it++){
		(*it)->startup();
	}
//for debug
/*	
	sleep(100);
	cout << "program going to stop"<< endl;
		for (vector<dispatch_server *>::iterator it = g_svr_vec.begin(); it != g_svr_vec.end(); it++) {
		(*it)->stop();
		(*it)->waitend();
		delete *it;
	}

	stop = true;
*/
	while(!stop){ sleep(3); }
}

vector<dispatch_server *> g_svr_vec;
void sig_stop(int signo)
{
	LOG_INFO_VA("stop servers in SIGINT ");
	signal(SIGINT,SIG_IGN);
	for (vector<dispatch_server *>::iterator it = g_svr_vec.begin(); it != g_svr_vec.end(); it++) {
		(*it)->stop();
		(*it)->waitend();
		delete *it;
	}
	stop = true;
	//exit(0);
}

int main(int argc, char **argv)
{
	if (argc != 2) {
		cout << "no configure file !" << endl;
		return -1;
	}

	xmlInitParser();
	lightxquery xqry;
	if (!xqry.load(argv[1])) {
		cout << "load config xml file error, please check " << argv[1] << endl;
		return -1;
	}
	
	// init logger
	string path, log_level;
	if (xqry.get_attr_value("/dumpcalc/public/log[@path]", "path", path) != 0) {
		cout << "log path configure error" << endl;
		return -1;
	}
	
	if(access(path.c_str(),W_OK) != 0)
	{
		cout << "log path can't write" << endl;
		return -1;
	}

	if (xqry.get_attr_value("/dumpcalc/public/log[@level]", "level", log_level) != 0) {
		cout << "log level configure error" << endl;
		return -1;
	}

	if (!init_logger(path, log_level)) {
		cout << "logger init failed, check /public/log[@path | @level]" << endl;
		return -1;
	}

	signal(SIGPIPE,SIG_IGN);
	signal(SIGINT, sig_stop);

	if(start_servers(xqry, g_svr_vec) == -1)
		return -1;
	
	return 0;
}

